package pl.enigmatic.parser.xml;

import org.w3c.dom.Node;

import pl.enigmatic.util.Arrays;
import pl.enigmatic.util.Strings;


@SuppressWarnings("rawtypes")
public class NestedPropertyExtension implements XMLParserExtension {

	static class MyNodeNameInfo extends NodeNameInfo {

		private final PropertyGetter[] getters;
		private final String[] path;
		private final PropertySetter setter;

		public MyNodeNameInfo(final PropertyGetter[] chain, final String[] path, Parser parser, PropertySetter setter) {
			super(path[path.length - 1], parser.getClazz(), parser);
			getters = chain;
			this.path = path;
			this.setter = setter;
		}

		public PropertyGetter[] getGetters() {
			return getters;
		}

		public String[] getPath() {
			return path;
		}

		public PropertySetter getSetter() {
			return setter;
		}
	}

	private final Parser<?> owner;

	public NestedPropertyExtension(final Parser<?> parser) {
		owner = parser;
	}

	@SuppressWarnings("unchecked")
	@Override
	public NodeNameInfo parseNodeName(final Node node, final ParsingInfo<?> info) {
		if (node.getPrefix() == null) {
			final String[] path = node.getNodeName().split("\\.");
			if (path.length < 2) { return null; }
			for (final String x : path) {
				if (Strings.upperStarted(x)) { return null; }
			}
			final PropertyGetter[] chain = new PropertyGetter[path.length - 1];
			Parser parser = owner;
			int i = 0;
			for (; i < path.length - 1; i++) {
				path[i] = parser.getPropertyName(path[i]);
				chain[i] = parser.getPropertyGetter(path[i]);
				final Class cls = parser.getPropertyType(path[i]);
				if (cls == null || (parser = Parser.createParser(cls)) == null) { return null; }
			}
			path[i] = parser.getPropertyName(path[i]);
			PropertySetter setter = parser.accessor(path[i]);
			if (setter == null) return null;
			String clazz = NodeNameInfo.getFullClassName(node, info.getStaticInfo());
			parser = clazz == null ? setter.getParser() : Parser.createParser(clazz);
			return new MyNodeNameInfo(chain, Arrays.changeSize(path, path.length - 1), parser, setter);
		}
		return null;
	}

	@Override
	public boolean setParsedProperty(final Object target, final ParsingInfo<?> info, final Object value) {
		MyNodeNameInfo name = (MyNodeNameInfo) info.getNodeInfo();
		Object trg = target;
		String[] path = name.getPath();
		PropertyGetter[] getters = name.getGetters();
		for (int i = 0; i < getters.length; i++) {
			trg = getters[i].getPropertyValue(trg, path[i]);
		}
		return name.getSetter().access(trg, value, info);
	}

	@Override
	public PropertySetter accessor(final String property) {
		return null;
	}

}
